package com.hk.commons.util;

import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.RandomUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.IntStream;

/**
 * 抢红包工具类
 *
 * @author Kevin
 * @date 2021-12-05 22:34
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class RedPacketUtils {

    /**
     * 红包拆解，将一个总金额大小的红包，拆解为多个小红包
     *
     * @param total 红包总金额
     * @param count 要拆解的个数
     * @return 拆解后的红包
     */
    public static int[] splitRedPacket(int total, int count) {
        int use = 0;
        int[] array = new int[count];
        for (int i = 0; i < count; i++) {
            if (i == count - 1) {
                array[i] = total - use;
            } else {
                int avg = (total - use) << 1 / (count - i); //尽可能平均拆解
                array[i] = 1 + RandomUtils.secure().randomInt(0, avg - 1);
            }
            use = use + array[i];
        }
        return array;
    }

    /**
     * 根据概率配置，生成要中奖值的数组
     *
     * @param count         要生成的中奖值总个数
     * @param probabilities 中奖值与概率关系
     */
    public static Integer[] randomRangeProbabilityRedPacket(int count, List<RangeProbability> probabilities) {
        if (count == 0) {
            return new Integer[0];
        }
        int sum = probabilities.stream()
                .filter(item -> item.getProbability() > 0)
                .mapToInt(RangeProbability::getProbability).sum();
        List<Integer> list = new ArrayList<>(count);
        for (RangeProbability item : probabilities) {
            if (item.getProbability() > 0) {
                int num = count * item.getProbability() / sum;
                list.addAll(IntStream.generate(() -> RandomUtils.secure().randomInt(item.getMinValue(), item.getMaxValue() + 1))
                        .limit(num)
                        .boxed()
                        .toList());
            }
        }
        int disparity = count - list.size(); // 如果不足总数， 从概率最高的开始补
        if (disparity > 0) {
            List<RangeProbability> sortList = probabilities.stream()
                    .filter(item -> item.getProbability() > 0)
                    .sorted(Comparator.comparingInt(RangeProbability::getProbability).reversed()).toList();
            for (int i = 0; i < disparity; i++) {
                var rangeProbability = sortList.get(i);
                list.add(RandomUtils.secure().randomInt(rangeProbability.getMinValue(), rangeProbability.getMaxValue() + 1));
            }
        }
        Collections.shuffle(list); //打乱顺序
        return list.toArray(Integer[]::new);
    }

    /**
     * 根据概率配置，生成要中奖值的数组
     *
     * @param count         要生成的中奖值总个数
     * @param probabilities 中奖值与概率关系
     */
    public static Integer[] randomFixedProbabilityRedPacket(int count, List<Probability> probabilities) {
        if (count == 0) {
            return new Integer[0];
        }
        List<Integer> list = new ArrayList<>(count);
        int sum = probabilities.stream()
                .filter(item -> item.getProbability() > 0)
                .mapToInt(Probability::getProbability).sum();
        for (Probability item : probabilities) {
            if (item.getProbability() > 0) {
                int num = count * item.getProbability() / sum;
                list.addAll(IntStream.generate(item::getValue).limit(num).boxed().toList());
            }
        }
        int disparity = count - list.size(); // 如果不足总数， 从概率最高的开始补
        if (disparity > 0) {
            List<Probability> sortList = probabilities.stream()
                    .filter(item -> item.getProbability() > 0)
                    .sorted(Comparator.comparingInt(Probability::getProbability).reversed()).toList();
            for (int i = 0; i < disparity; i++) {
                list.add(sortList.get(i).getValue());
            }
        }
        Collections.shuffle(list); //打乱顺序
        return list.toArray(Integer[]::new);
    }

    /**
     * 中奖值与概率关系
     */
    @Data
    @NoArgsConstructor
    @Accessors(chain = true)
    public static class Probability implements Serializable {

        /**
         * 中奖值
         */
        private int value;

        /**
         * 中奖概率百分百
         */
        private int probability;
    }


    /**
     * 中奖值与概率关系
     */
    @Data
    @NoArgsConstructor
    @Accessors(chain = true)
    public static class RangeProbability implements Serializable {

        /**
         * 中奖值最小值
         */
        private int minValue;

        /**
         * 中奖值最大值
         */
        private int maxValue;

        /**
         * 中奖概率百分百
         */
        private int probability;
    }


}
